﻿using System;
using System.Collections.Generic;
using System.Text;
using NLBS.Framework.Interfaces;

using System.Collections.Specialized;

namespace NLBS.Syndication
{
    /// <summary>
    /// Abstract base class used to write RSS feeds.
    /// </summary>
    public abstract class BaseRssWriter<T> : BaseSyndicationWriter<T>
    {
        private bool isBuilt = false;

        /// <summary>
        /// Creates a new <see cref="BaseRssWriter"/> instance.
        /// </summary>
        /// <param name="dateLastViewedFeedItemPublished">Last viewed feed item.</param>
        protected BaseRssWriter(DateTime dateLastViewedFeedItemPublished, bool useDeltaEncoding)
            : base(dateLastViewedFeedItemPublished, useDeltaEncoding)
        {
        }

        /// <summary>
        /// Builds the RSS feed.
        /// </summary>
        protected override void Build()
        {
            Build(this.DateLastViewedFeedItemPublished);
        }

        /// <summary>
        /// Builds the specified last id viewed.
        /// </summary>
        /// <param name="dateLastViewedFeedItemPublished">Last id viewed.</param>
        protected override void Build(DateTime dateLastViewedFeedItemPublished)
        {
            if (!isBuilt)
            {
                StartDocument();
                SetNamespaces();
                StartChannel();
                WriteChannel();
                WriteEntries();
                EndChannel();
                EndDocument();
                isBuilt = true;
            }
        }
        /// <summary>
        /// Starts the RSS document.
        /// </summary>
        protected virtual void StartDocument()
        {
            this.WriteStartElement("rss");
            this.WriteAttributeString("version", "2.0");
        }

        /// <summary>
        /// Sets the namespaces used within the RSS feed.
        /// </summary>
        protected virtual void SetNamespaces()
        {
            this.WriteAttributeString("xmlns:wfw", "http://wellformedweb.org/CommentAPI/");
        }

        /// <summary>
        /// Writes the channel Start element.
        /// </summary>
        protected void StartChannel()
        {
            this.WriteStartElement("channel");
        }

        /// <summary>
        /// Writes the channel.
        /// </summary>
        protected virtual void WriteChannel()
        {
            //RssImageElement image = new RssImageElement(GetRssImage(), info.Title, info.HomeFullyQualifiedUrl, 77, 60, null);
            BuildChannel(blogInfo.Title, blogInfo.BaseUrl, blogInfo.Email, blogInfo.SubTitle, blogInfo.Language, blogInfo.BlogWebMaster, blogInfo.SubTitle);
        }

        /// <summary>
        /// Ends the document.
        /// </summary>
        protected void EndDocument()
        {
            this.WriteEndElement();
        }

        /// <summary>
        /// Builds the RSS channel starting XML section.
        /// </summary>
        /// <param name="title">The title.</param>
        /// <param name="link">The link.</param>
        /// <param name="authorEmail">The author email.</param>
        /// <param name="description">The description.</param>
        /// <param name="lang">The lang.</param>
        /// <param name="copyright">The copyright.</param>
        /// <param name="cclicense">The cclicense.</param>
        /// <param name="image">An optional sub-element of channel for rendering an image for the channel.</param>
        protected void BuildChannel(string title, string link, string authorEmail, string description, string lang, string copyright, string cclicense)
        {
            //Required Channel Elements
            this.WriteElementString("title", title);
            this.WriteElementString("link", link);
            this.WriteElementString("description", description);

            //Optional Channel Elements
            this.WriteElementString("language", lang);
            //TODO: Implement this element.
            this.WriteElementString("copyright", copyright);

            //TODO: Provide REAL email authentication.
            //TODO: Allow blog owner to omit this field on a per-blog basis without having to remove email address. Or we might consider a separate field for Syndicated email address.
            if (authorEmail != null && authorEmail.Length > 0 && authorEmail.IndexOf("@") > 0 && authorEmail.IndexOf(".") > 0)
            {
                this.WriteElementString("webMaster", authorEmail);
            }

            this.WriteElementString("generator", "NLBS v1.0");

            this.WriteElementString("pubDate", DateTime.Now.ToString());
            this.WriteElementString("ttl", "60");
        }

        protected void EndChannel()
        {
            this.WriteEndElement();
        }

        private void WriteEntries()
        {
            foreach (T entry in this.Items)
            {
                this.WriteStartElement("item");
                EntryXml(entry);
                this.WriteEndElement();
            }
        }

        /// <summary>
        /// Writes the XML for a single entry.
        /// </summary>
        /// <param name="item">Entry.</param>
        /// <param name="settings">Settings.</param>
        /// <param name="urlFormats">Uformat.</param>
        protected virtual void EntryXml(T item)
        {

            //core
            this.WriteElementString("title", GetTitleFromItem(item));
            this.WriteElementString("link", GetLinkFromItem(item));
            if(!IsComment) this.WriteElementString("category", GetCategoriesFromItem(item));
            this.WriteElementString("author", GetAuthorFromItem(item));
            this.WriteElementString("pubDate", GetPublishedDateUtc(item).ToString("r"));
            this.WriteElementString("description", GetBodyFromItem(item));
            if (!IsComment) this.WriteElementString("wfw:commentRss", GetCommentRssLink(item));
            //this.WriteElementString("guid", "http://www.voidland.com/blog/feed.asp?q=comment&id=89");
        }
        /// <summary>
        /// Gets the categories from entry.
        /// </summary>
        /// <param name="item">The entry.</param>
        /// <returns></returns>
        protected abstract bool IsComment
        {
            get;
        }


        /// <summary>
        /// Gets the categories from entry.
        /// </summary>
        /// <param name="item">The entry.</param>
        /// <returns></returns>
        protected abstract string GetCategoriesFromItem(T item);

        /// <summary>
        /// Gets the title from item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract string GetTitleFromItem(T item);

        /// <summary>
        /// Gets the link from item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract string GetLinkFromItem(T item);

        /// <summary>
        /// Gets the body from item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract string GetBodyFromItem(T item);

        /// <summary>
        /// Gets the author from item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract string GetAuthorFromItem(T item);

        /// <summary>
        /// Gets the publish date from item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract DateTime GetPublishedDateUtc(T item);

        /// <summary>
        /// Returns true if the Item could contain comments.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract bool ItemCouldContainComments(T item);

        /// <summary>
        /// Returns true if the item allows comments, otherwise false.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract bool ItemAllowsComments(T item);

        /// <summary>
        /// Returns true if comments are closed, otherwise false.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract bool CommentsClosedOnItem(T item);

        /// <summary>
        /// Gets the feedback count for the item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        protected abstract int GetFeedbackCount(T item);

        /// <summary>
        /// Obtains the syndication date for the specified entry, since 
        /// we don't necessarily know if the type has that field, we 
        /// can delegate this to the inheriting class.
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        protected abstract DateTime GetSyndicationDate(T item);

        protected abstract string GetCommentRssLink(T item);
    }
}
